from django.http import HttpResponse, Http404
from django.views.generic.detail import BaseDetailView
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from shop.models import Category, DessertItem, Sale, SaleEntry
from core.views import extract_date, member_only, potential_member_only
import datetime
import json


def _get_sales(member, is_reservation=False):
    sales = Sale.objects.filter(customer=member, is_paid=True, is_reservation=is_reservation).order_by('-paid_time')
    return sales

def _get_unpaid_sale(member):
    unpaid = Sale.objects.filter(customer=member, is_paid=False)
    sale = unpaid[0] if unpaid else None
    return sale

@potential_member_only
def browse(request):
    categories = Category.objects.all()
    return render_to_response('shop/browse.html', {'categories': categories}, context_instance=RequestContext(request))

@member_only
def cart(request):
    member = request.user.info
    params = {}
    sale = _get_unpaid_sale(member)
    params['sale'] = sale
    params['section'] = 'C'

    # prepare param for Purchase section
    purchases = _get_sales(member)
    params['purchases'] = purchases
    if request.GET.get('dest', '') == 'purchase':
        params['section'] = 'P'

    # prepare param for Reservation section
    reservations = _get_sales(member, is_reservation=True) 
    params['reservations'] = reservations
    if request.GET.get('dest', '') == 'reservation':
        params['section'] = 'R'

    return render_to_response('shop/cart.html', params, context_instance=RequestContext(request))

@member_only
def checkout(request):
    member = request.user.info
    sale = _get_unpaid_sale(member)
    # current member has no unpaid sale
    if not sale:
        return redirect('/shop/cart')
    # current member's deposit is not enough
    if member.balance < sale.total():
        return redirect('/recharge')
    # update member balance
    member.balance -= sale.total()
    member.save()

    # update sale
    sale.is_paid = True
    sale.paid_time = datetime.datetime.now()
    sale.save()
    return redirect('/shop/cart?dest=purchase')

@member_only
def reserve(request):
    # if not enough deposit, redirect to recharge
    # dont forget to round the first payment!
    member = request.user.info
    sale = _get_unpaid_sale(member)
    # current member has no unpaid sale
    if not sale:
        return redirect('/shop/cart')
    # current member's deposit is not enough
    payment = sale.first_round_payment()
    if member.balance < payment:
        return redirect('/recharge')
    # update member balance
    member.balance -= payment
    member.save()

    today = datetime.date.today()
    tomorrow = today + datetime.timedelta(days=1)

    # update sale
    sale.is_reservation = True
    sale.is_paid = True
    sale.paid_time = datetime.datetime.now()
    sale.get_day = tomorrow
    sale.save()

    message = 'You can specify the get day of this reservation. You can skip this step and the get day will default to tomorrow'
    return render_to_response('shop/getday.html', {'pk': sale.pk, 'default_day': tomorrow, 'message': message}, context_instance=RequestContext(request))

@member_only
def set_getday(request):
    if request.method != 'POST':
        return redirect('/shop/cart')
    # validation 1: pk should exist
    pk = request.POST.get('pk', '')
    if not pk:
        return redirect('/shop/cart')

    today = datetime.date.today()
    tomorrow = today + datetime.timedelta(days=1)
    date = extract_date(request.POST.get('getday', ''))
    # validation 2: getday parameter shoule be in valid date format
    if not date:
        message = 'You should enter a valid date in format yyyy-mm-dd. We\'ve set tomorrow as default for you. :)'
        return render_to_response('shop/getday.html', {'pk': pk, 'default_day': tomorrow, 'message': message}, context_instance=RequestContext(request))

    # validation 3: the date should be at least tomorrow
    if date <= today:
        message = 'Get day should be at least tomorrow!'
        return render_to_response('shop/getday.html', {'pk': pk, 'default_day': tomorrow, 'message': message}, context_instance=RequestContext(request))

    res = Sale.objects.filter(pk=pk, is_reservation=True, is_paid_off=False)
    # validation 4: the Sale object should exist
    if not res:
        return redirect('/shop/cart')

    # finally, we are safe to set get_day
    sale = res[0]
    sale.get_day = date
    sale.save()
    return redirect('/shop/cart?dest=reservation')
    
@member_only
def payoff(request, pk):
    member = request.user.info
    res = Sale.objects.filter(pk=pk, customer=member, is_reservation=True, is_paid=True, is_paid_off=False)
    sale = res[0] if res else None
    if not sale:
        return redirect('/shop/cart?dest=reservation')

    payment = sale.second_round_payment()
    if member.balance < payment:
        return redirect('/recharge')
    # update uer balance
    member.balance -= payment
    member.save()

    # update sale
    sale.is_paid_off = True
    sale.paid_off_time = datetime.datetime.now()
    sale.save()

    return redirect('/shop/cart?dest=reservation')

@member_only
def check_deposit(request):
    if not request.is_ajax():
        return redirect('/shop/cart')

    action = request.GET.get('for', '')
    # get basic information
    member = request.user.info
    deposit = member.balance

    # deal with payoff carefully
    if 'payoff' == action:
        # INTENTIONALLY waitint exception
        # the data GET is invalid
        pk = request.GET['pk']
        sale = Sale.objects.get(pk=pk, customer=member, is_reservation=True, is_paid=True, is_paid_off=False)
        total = sale.second_round_payment()
    else:
        sale = _get_unpaid_sale(member)
        # deal with reservation
        if 'reserve' == action:
            total = round(sale.first_round_payment()/2, 2)
        # the normal sale
        else:
            total = sale.total()

    # generate response data
    data = {
        'not_enough': deposit < total,
        'total': total,
    }
    return HttpResponse(json.dumps(data))

@member_only
def add_to_cart(request):
    member = request.user.info
    if request.method != 'POST':
        raise Http404('This request is POST only')
    # 1. check if current member has unpaid Sale, create one if not
    sale = _get_unpaid_sale(member)
    print sale
    if not sale:
        rate = member.level.discount_rate
        sale = Sale.objects.create(customer=member, discount_rate=rate)

    # 2. create SaleEntry and add it to Sale
    #WARNING: this (request.raw_post_data)  would be incompatible with django 1.4!
    data = json.loads(request.raw_post_data)
    try:
        item = DessertItem.objects.get(pk=data['pk'])
        quantity = int(data['quantity'])
    except:
        raise Http404('Corrupted POST data')

    item.stock -= quantity
    item.save()
    entry = SaleEntry.objects.create(
        item = item,
        sale = sale,
        quantity = quantity,
        price = item.price
    )
    return HttpResponse('good')

# refer: https://docs.djangoproject.com/en/dev/topics/class-based-views/
class JSONItemResponseMixin:
    def render_to_response(self, context):
        item = context.get('item', None)
        data = {
            'name': item.name,
            'pic_url': item.pic.url,
            'detail': item.detail,
            'stock': item.stock,
            'price': item.price,
            'pk': item.pk
        }
        return HttpResponse(json.dumps(data), mimetype='application/json')

class JSONItemView(JSONItemResponseMixin, BaseDetailView):
    model = DessertItem

    def get_context_data(self, **kwargs):
        context = super(JSONItemView, self).get_context_data(**kwargs)
        obj = super(JSONItemView, self).get_object()
        context['item'] = obj
        return context
